#pragma once

#include <iostream>
#include "Socket.hpp"
#include "log.hpp"
#include <pthread.h>
#include<fstream>
#include<vector>
#include<unordered_map>
#include<sstream>

static const std::string sep = "\r\n";
static const uint16_t defaultport = 8888;
static const std::string webroot = "./wwwroot";
static const std::string homepage = "index.html";

class HttpServer;

struct ThreadData
{
    ThreadData(int sockfd,HttpServer* psvr) : sockfd_(sockfd),psvr_(psvr)
    {}

    HttpServer* psvr_;
    int sockfd_;
};


class HttpRequest
{
public:
    HttpRequest()
    {}

    void DeSerialize(std::string package)
    {
        while(true)
        {
            auto pos = package.find(sep);
            if(pos == std::string::npos) break;
            std::string tmp = package.substr(0,pos);
            package.erase(0,pos+sep.size());
            if(tmp.empty()) break;
            req_header_.push_back(tmp);
        }
        text = package;
    }

    void parse()
    {
        std::stringstream ss(req_header_[0]);
        ss >> method_ >> url_ >> http_ver_;

        file_path_ += webroot;// ./wwwroot
        if(url_ == "/" || url_ == "/index.html")
        {
            file_path_ += "/";
            file_path_ += homepage; //./wwwroot/index.html
        }
        else
        {
            file_path_ += url_;
        }

        auto pos = file_path_.rfind(".");
        if(pos == std::string::npos) suffix = ".html";
        else suffix = file_path_.substr(pos);
    }

    ~HttpRequest()
    {}


public:
    std::vector<std::string> req_header_;
    std::string text;

    std::string method_;
    std::string url_;
    std::string http_ver_;
    std::string file_path_;
    
    std::string suffix;
};



class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport) : port_(port)
    {
        content_type_.insert({".html","text/html"});
        content_type_.insert({".png","image/png"});
        content_type_.insert({".jpg","image/jpeg"});
    }

    void Init()
    {
        listen_sock_.Socket();
        listen_sock_.Bind(port_);
        listen_sock_.Listen();
        lg(Info, "Init access!!!");
    }

    std::string SuffixToDesc(const std::string& suffix)
    {
        auto iter = content_type_.find(suffix);
        if(iter == content_type_.end()) return "text/html";
        else return iter->second;
    }

    void Start()
    {
        for (;;)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = listen_sock_.Accept(clientip, clientport);
            if(sockfd < 0)
            {
                lg(Warning, "accept error!!!!");
                continue;
            }
            lg(Info, "get a new link,sockfd: %d", listen_sock_.GetSockfd());
            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd,this);
            pthread_create(&tid, nullptr, RouTine, td);
        }
    }

    static void *RouTine(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        // char buffer[10240];
        // ssize_t n = recv(td->sockfd_,buffer,sizeof(buffer)-1,0);
        // if(n > 0)
        // {
        //     buffer[n] = 0;
        //     std::cout << buffer;
        // }
        td->psvr_->HandleHttp(td->sockfd_);
        close(td->sockfd_);
        delete (td);
        return nullptr;
    }



    static std::string ReadHtmlContent(const std::string& file_path)
    {
        //这个版本不能够读取二进制文件内容，比如图片，音频，视频
        // std::ifstream in(file_path);
        // if(!in.is_open()) return "";

        // std::string content;
        // std::string line;
        // while(std::getline(in,line))
        // {
        //     content += line;
        // }

        std::ifstream in(file_path, std::ios::binary);
        if(!in.is_open()) return "";

        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::beg);

        std::string content;
        content.resize(len);

        in.read((char*)content.c_str(), content.size());
        in.close();

        return content;
    }

    void HandleHttp(int sockfd)
    {
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer), 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer;

            //正文内容现在是固定的，但也可以是html文件内容，由http自动解析
            // std::string text = "hello world";
            //version1: 无论请求方请求哪个资源，我们都固定返回hello.html
            // std::string text;
            // text = ReadHtmlContent("./wwwroot/hello.html");

            //version2
            //解析我们的http请求方
            // HttpRequest req;
            // req.DeSerialize(buffer);
            // req.parse();
            // std::string text;
            // text = ReadHtmlContent(req.file_path_);
            // std::string response_line;
            // response_line += "HTTP/1.0 200 YES";


            //version3
            //解析我们的请求方各种字段，并且在响应做出判断
            HttpRequest req;
            req.DeSerialize(buffer);
            req.parse();
            std::string text;
            bool is_right_path = true;
            text = ReadHtmlContent(req.file_path_);
            if(text.empty())
            {
                is_right_path = false;
                std::string err_html = webroot;
                err_html += "/err.html";
                text = ReadHtmlContent(err_html);
            }
            std::string response_line;
            if(is_right_path)  response_line += "HTTP/1.0 200 YES";
            else response_line += "HTTP/1.0 404 Not Found";


            //状态行状态码3xx情况
            // response_line = "HTTP/1.0 302 Redirect ";

            response_line += sep;
            std::string response_head = "Content-Length: text/html";
            //response_head += std::to_string(text.size());
            response_head += sep;
            //加一个字段判别正文类型，方便回显不同文件类型]
            response_head += "Content-Type: ";
            response_head += SuffixToDesc(req.suffix);
            response_head += sep;

            //状态行状态码3xx情况，替换网页：
            // response_head += "Location: https://www.qq.com/";
            // response_head += sep;

            //加一个字段向浏览器保存我们的cookie信息
            //一般是由浏览器向服务器提交信息时保存用的，分为内存和磁盘保存，但是我们这里不从浏览器读取了，直接回显一个信息让浏览器保存cookie信息
            response_head += "Set-Cookie: name=user&&passwd=123";
            response_head += sep;

            std::string blank_line = "\r\n";

            std::string response = response_line;
            response += response_head;
            response += blank_line;
            response += text;

            send(sockfd, response.c_str(), response.size(), 0);
        }
    }

    ~HttpServer()
    {
    }

private:
    Sock listen_sock_;
    uint16_t port_;
    std::unordered_map<std::string,std::string> content_type_;
};